<script setup lang="tsx">
import type { defineComponent } from 'vue'
import type { FormExpose } from '../../Form'
import type { TableColumn, TableExpose } from '../../Table'
import type { ETableProps, ETableRowData } from './types'
import {
  ElButton,
  ElFormItem,
  ElMessage,
  ElMessageBox,
  ElTour,
  ElTourStep,
} from 'element-plus'
import { useDesign, useValidator } from 'fetm-hooks'
import { guid } from 'fetm-utils'
/**
 * 可编辑表格（支持多列），双向绑定数组，不支持被Form组件引用到schema
 * 特性：支持jsplumb端点，支持渲染Form组件的输入组件
 */
import { computed, reactive, ref, unref, watchEffect } from 'vue'
import { Form } from '../../Form'
import { Table } from '../../Table'
import { useRenderCheckbox } from './components/useRenderCheckbox'
import { useRenderRadio } from './components/useRenderRadio'
import { useRenderSelect } from './components/useRenderSelect'
import {
  setComponentProps,
  setItemComponentSlots,
  setTextPlaceholder,
} from './helpers'
import { componentMap } from './helpers/componentMap'
import { ETableComponentNameEnum } from './types'

const props = withDefaults(defineProps<ETableProps>(), {
  hiddenForm: false,
  hiddenAction: false,
  hiddenAdd: false,
  hiddenBatchDelete: false,
  hiddenEdit: false,
  hiddenDelete: false,
  hiddenSelection: false,
  hiddenIndex: false,
  validateRequired: false,
  maxLine: 9999,
  rowKey: 'internal_id',
  disabled: false,
  columns: () => [],
})

const emits = defineEmits<{
  (e: 'saveRow', value: { row: any, internalId: string, index: number }): void
  (e: 'deleteRow', value: { row: any, internalId: string, index: number }): void
  (e: 'deleteBatch', value: { rows: any[], internalIds: string[] }): void
}>()

const attrs = useAttrs()
const slots = useSlots()

const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('e-table')
const uuidCls = `${prefixCls}__${guid()}`

const { renderSelectOptions } = useRenderSelect()
const { renderRadioOptions } = useRenderRadio()
const { renderCheckboxOptions } = useRenderCheckbox()

const modelValue = defineModel<Record<string, any>[]>('modelValue', {
  default: [],
})

// #region ************************************form*********************************************
const formRef = ref<FormExpose>()
const formModel = reactive<Record<string, any>>({})
const rules = reactive({})
const { required } = useValidator()
// #endregion *************************************************************************************

// #region ************************************table*********************************************
const tableRef = ref<TableExpose>()
const tableData = ref<ETableRowData[]>([])

// 初始化tableData
watchEffect(() => {
  tableData.value = modelValue.value.map((item) => {
    return {
      ...item,
      internal_id: item.internal_id ?? guid(),
      internal_disabled: item.internal_disabled ?? true,
      internal_disableSelection: item.internal_disableSelection ?? false,
      internal_hiddenAction: item.internal_hiddenAction ?? false,
      internal_hiddenEdit: item.internal_hiddenEdit ?? false,
      internal_hiddenDelete: item.internal_hiddenDelete ?? false,
    }
  })
})

watchEffect(() => {
  tableData.value.forEach((item, index) => {
    props.columns.forEach((col) => {
      formModel[`${col.field}__${index}`] = item[col.field]
      rules[`${col.field}__${index}`]
        = col.rules ?? (props.validateRequired ? [required()] : [])
    })
  })
})

const tableProps = computed(() => {
  return {
    rowKey: props.rowKey,
    rowClassName: ({ row, rowIndex }) => {
      return `e-table-row__${row[props.rowKey]} relative`
    },
    showOverflowTooltip: false,
  }
})

const defaultPrevColumns = computed(() => {
  return [
    {
      hidden: props.hiddenSelection,
      field: 'internal_selection',
      type: 'selection',
      fixed: true,
      selectable: row => Boolean(!row.internal_disableSelection),
    },
    {
      hidden: props.hiddenIndex,
      field: 'internal_index',
      type: 'index',
      label: '序号',
      fixed: true,
    },
  ]
})

const defaultLastColumns = computed(() => {
  return [
    {
      hidden: props.hiddenAction,
      field: 'action',
      label: '操作',
      fixed: 'right',
      minWidth: 140,
      slots: {
        default: (scope) => {
          const rowDisabled = Boolean(scope.row.internal_disabled)
          const rowHiddenAction = Boolean(scope.row.internal_hiddenAction)
          const rowHiddenEdit = Boolean(
            scope.row.internal_hiddenEdit || props.hiddenEdit,
          )
          const rowHiddenDelete = Boolean(
            scope.row.internal_hiddenDelete || props.hiddenDelete,
          )
          return (
            <>
              {rowHiddenAction ? (
                ''
              ) : (
                <>
                  {rowHiddenEdit ? (
                    ''
                  ) : rowDisabled ? (
                    <ElButton
                      size="small"
                      type="warning"
                      disabled={props.disabled}
                      onClick={() => handleEditRow(scope.row, scope.$index)}
                    >
                      编辑
                    </ElButton>
                  ) : (
                    <ElButton
                      class={`${prefixCls}__save-btn`}
                      size="small"
                      type="primary"
                      disabled={props.disabled}
                      onClick={() => handleSaveRow(scope.row, scope.$index)}
                    >
                      保存
                    </ElButton>
                  )}
                  {rowHiddenDelete ? (
                    ''
                  ) : (
                    <ElButton
                      type="danger"
                      size="small"
                      disabled={props.disabled}
                      onClick={() => handleDeleteRow(scope.row, scope.$index)}
                    >
                      删除
                    </ElButton>
                  )}
                </>
              )}
            </>
          )
        },
      },
    },
  ]
})

const unionColumns = computed<TableColumn[]>(() => {
  return [
    ...defaultPrevColumns.value,
    ...props.columns.map((col) => {
      // 默认组件为Input
      const component = col.component ?? 'Input'
      return {
        ...col,
        component,
        slots: {
          default: (scope) => {
            console.log(scope.row[col.field])
            return (
              <>
                {!props.hiddenForm ? (
                  <ElFormItem prop={`${col.field}__${scope.$index}`} {...col.formItemProps}>
                    {{
                      default: () => {
                        if (col.slots?.edit) {
                          return col.slots?.edit(scope.row)
                        }
                        const Com = componentMap[component] as ReturnType<
                          typeof defineComponent
                        >
                        const componentSlots = col.componentProps?.slots || {}
                        const slotsMap: Record<string, any> = {
                          ...setItemComponentSlots(componentSlots),
                        }
                        // // 如果是select组件，并且没有自定义模板，自动渲染options
                        if (col.component === ETableComponentNameEnum.SELECT) {
                          slotsMap.default = !componentSlots.default
                            ? () => renderSelectOptions(col)
                            : () => {
                                return componentSlots.default(
                                  unref(col?.componentProps?.options),
                                )
                              }
                        }

                        // 虚拟列表
                        if (
                          col.component === ETableComponentNameEnum.SELECT_V2
                          && componentSlots.default
                        ) {
                          slotsMap.default = ({ item }: any) => {
                            return componentSlots.default(item)
                          }
                        }

                        // 单选框组和按钮样式
                        if (
                          col.component
                          === ETableComponentNameEnum.RADIO_GROUP
                          || col.component === ETableComponentNameEnum.RADIO_BUTTON
                        ) {
                          slotsMap.default = !componentSlots.default
                            ? () => renderRadioOptions(col)
                            : () => {
                                return componentSlots.default(
                                  unref(col?.componentProps?.options),
                                )
                              }
                        }

                        // 多选框组和按钮样式
                        if (
                          col.component
                          === ETableComponentNameEnum.CHECKBOX_GROUP
                          || col.component
                          === ETableComponentNameEnum.CHECKBOX_BUTTON
                        ) {
                          slotsMap.default = !componentSlots.default
                            ? () => renderCheckboxOptions(col)
                            : () => {
                                return componentSlots.default(
                                  unref(col?.componentProps?.options),
                                )
                              }
                        }

                        const Comp = () => {
                          return (
                            <Com
                              vModel={scope.row[col.field]}
                              disabled={
                                props.disabled || scope.row.internal_disabled
                              }
                              {...setTextPlaceholder(col)}
                              {...setComponentProps(col)}
                            >
                              {{ ...slotsMap }}
                            </Com>
                          )
                        }

                        return (
                          <div class={`w-full ${col.class ? col.class : ''}`}>
                            {Comp()}
                          </div>
                        )
                      },
                    }}
                  </ElFormItem>
                ) : (
                  <div class="line-height-32px">
                    {col.slots?.detail
                      ? col.slots?.detail(scope.row)
                      : (scope.row[col.field] ?? '--')}
                  </div>
                )}
              </>
            )
          },
          header: (scope) => {
            return col.slots?.header?.(scope) || col.label
          },
        },
      }
    }),
    ...defaultLastColumns.value,
  ] as TableColumn[]
})

/* 添加新的一行，默认是可编辑状态 */
function handleAddRow(row?: Record<string, any>) {
  if (props.maxLine !== undefined && tableData.value?.length >= props.maxLine) {
    ElMessage.warning('已达到最大行数')
    return
  }
  const obj: ETableRowData = {
    internal_id: guid(),
    internal_disabled: false,
    internal_disableSelection: false,
    internal_hiddenAction: false,
    internal_hiddenEdit: false,
    internal_hiddenDelete: false,
  }
  props.columns.forEach((item) => {
    obj[item.field] = row?.[item.field] ?? item.defaultValue
  })
  tableData.value.push(obj)
  return {
    data: obj,
    index: tableData.value.length - 1,
  }
}

/* 编辑 */
function handleEditRow(row, index) {
  row.internal_disabled = false
}
/* 保存 */
function handleSaveRow(row, index) {
  const fields = props.columns.map((col) => {
    return `${col.field}__${index}`
  })
  // validateField 只验证这一行而不是整体
  formRef?.value?.getElFormExpose()?.validateField(fields, (valid) => {
    if (valid) {
      row.internal_disabled = true
      modelValue.value = tableData.value
      emits('saveRow', { row, internalId: row.internal_id, index })
    }
  })
}
/* 删除 */
function handleDeleteRow(row, index) {
  ElMessageBox.confirm('确定删除吗?', '提示', {
    type: 'warning',
  }).then(async (e) => {
    tableData.value.splice(index, 1)
    modelValue.value = tableData.value
    emits('deleteRow', { row, internalId: row.internal_id, index })
  })
}

/* 批量删除 */
const ids = ref<string[]>([])
function handleDeleteBatch() {
  ids.value
    = tableRef.value?.elTableRef
      ?.getSelectionRows()
      .map((v: any) => v.internal_id) || []
  if (!ids.value?.length) {
    ElMessage.warning('请选择要删除的行')
    return
  }
  ElMessageBox.confirm('确定删除吗?', '提示', {
    type: 'warning',
  }).then(async (e) => {
    const rows = ids.value.map((id) => {
      return tableData.value.find(item => item.internal_id == id)
    })
    ids.value.forEach((id) => {
      tableData.value.splice(
        tableData.value.findIndex(item => item.internal_id == id),
        1,
      )
    })
    modelValue.value = tableData.value
    emits('deleteBatch', { rows, internalIds: ids.value })
  })
}
// #endregion *************************************************************************************
/* 获取 table 行 dom 元素 */
function getRowElementByRowKey(value: string) {
  const rowEl = document.querySelector(
    `.${prefixCls}.${uuidCls} .e-table-row__${value}`,
  )
  // important: 为table row 添加 id，否则jsPlumb的sourceId和targetId会内设默认值
  if (rowEl)
    rowEl.id = value

  return rowEl
}

const openTour = ref(false)
const tourList = ref<
  Array<{
    target: any
  }>
>()

function validated(options?: { isShowTour?: boolean, delay?: number, ignoreSave?: boolean }) {
  return new Promise<boolean>((resolve, reject) => {
    if (props.hiddenForm) {
      resolve(true)
    }
    else {
      formRef?.value
        ?.getElFormExpose()
        ?.validate((valid) => {
          if (!options?.ignoreSave && tableData.value.some(item => !item.internal_disabled)) {
            if (options?.isShowTour) {
              setTimeout(() => {
                tourList.value = []
                document
                  .querySelectorAll(`.el-table__body .${prefixCls}__save-btn`)
                  ?.forEach((item) => {
                    tourList.value?.push({
                      target: item,
                    })
                  })
                openTour.value = true
              }, options.delay ?? 500)
            }
            reject(new Error('请先保存正在编辑的表单项'))
          }
          if (valid)
            resolve(valid)
          else reject(new Error('请处理表单项规则验证问题'))
        })
        .catch((e) => {
          reject(e)
        })
    }
  })
}
defineExpose({
  handleAddRow,
  handleEditRow,
  handleSaveRow,
  handleDeleteRow,
  getModelValueRef: () => modelValue,
  getTableDataRef: () => tableData,
  getRowElementByRowKey,
  validated,
})
</script>

<template>
  <div class="w-full p-20px" :class="[prefixCls, uuidCls]">
    <div v-if="!hiddenAdd || !hiddenBatchDelete || slots.header" class="mb-10px">
      <ElButton v-if="!hiddenAdd" type="primary" @click="handleAddRow()">
        添加一行
      </ElButton>
      <ElButton
        v-if="!hiddenBatchDelete"
        type="danger"
        @click="handleDeleteBatch()"
      >
        批量删除
      </ElButton>
      <slot name="header" />
    </div>
    <Form
      v-if="!hiddenForm"
      ref="formRef"
      :model="formModel"
      :rules="rules"
      :validate-on-rule-change="false"
      :label-width="0"
      is-custom
    >
      <Table
        ref="tableRef"
        :columns="unionColumns"
        :data="tableData"
        v-bind="{
          ...tableProps,
          ...attrs,
        }"
      />
    </Form>
    <Table
      v-else
      ref="tableRef"
      :columns="unionColumns"
      :data="tableData"
      v-bind="{
        ...tableProps,
        ...attrs,
      }"
    />
    <ElTour v-model="openTour">
      <ElTourStep
        v-for="(item, index) in tourList"
        :key="index"
        :target="item.target"
        title="点击这里"
        description="保存正在编辑的表单项"
      />
    </ElTour>
  </div>
</template>

<style lang="less" scoped>
@prefix-cls: ~'fetm-e-table';
.@{prefix-cls} {
  :deep(.el-form-item--default) {
    margin-bottom: 0 !important;
  }
  :deep(.el-form-item--default .el-form-item__error) {
    top: unset;
    left: unset;
    right: 10px;
  }
  :deep(.el-form-item__content) {
    min-width: unset !important;
    width: 100%;
  }
  :deep(.el-form-item__error) {
    z-index: 1;
  }
  :deep(.el-table .cell) {
    overflow: visible;
  }
}
</style>
